﻿using mozhi.smarterp.Finance.Accounts;
using mozhi.smarterp.Finance.Accounts.Repository;
using mozhi.smarterp.Finance.Accounts.Services;
using mozhi.smarterp.Finance.Basedatas.Repository;
using mozhi.smarterp.Finance.Basedatas.Services;
using mozhi.smarterp.Finance.Finances.Repository;
using mozhi.smarterp.Finance.Finances.Services;
using mozhi.smarterp.Finance.Querys;
using mozhi.smarterp.Finance.Reports.Repository;
using Mozhi.Abp.Core.Extensions;
using Mozhi.Abp.Domain.BasicData;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;

namespace mozhi.smarterp.Finance.Reports
{
    public class BalanceSheetAppService : FinancialReportAppService, IBalanceSheetReportAppService
    {
        private IFinancialReportItemRepository _financialReportItemRepository;

        public BalanceSheetAppService(IAccountRepository accountRepository, IDimensionGroupValueRepository dimensionGroupValueRepository, ICurrencyRepository currencyRepository, 
            ISettlementRepository settlementRepository, IAccountBalanceRepository accountBalanceRepository,
            AccountBalanceManager accountBalanceManager, DimensionGroupValueManager dimensionGroupValueManager,
            IOrganizationRepository organizationRepository, IEndPeriodProcessResultRepository endPeriodProcessResultRepository,
            IJournalRepository journalRepository, JournalManager journalManager, IFinancialReportItemRepository financialReportItemRepository) :
            base(accountRepository, dimensionGroupValueRepository, currencyRepository, settlementRepository, accountBalanceRepository,
                accountBalanceManager, dimensionGroupValueManager, organizationRepository, endPeriodProcessResultRepository, journalRepository, journalManager)
        {
            _financialReportItemRepository = financialReportItemRepository;
        }

        public async Task<List<BalanceSheetReportDto>> GetData(BalanceSheetReportQuery query)
        {
            //var result = new List<BalanceSheetReportDto>();

            //if (query == null || query.Period == 0)
            //    throw new UserFriendlyException("查询参数不能为空，并且期间不能为空", "100000");

            ////当前期间的余额
            //var balances = await GetAccountBalancesAsync(new ReportQueryBase() { StartPeriod = query.Period, EndPeriod = query.Period, IncludeDimension = false, IncludeCurrency = false, IncludeUnApproveJournal = query.IncludeUnApproveJournal });

            //var accounts = await _accountRepository.FindAsync(new AccountQuery());

            //var organization = await GetOrganizationAsync();

            ////年初的科目余额
            //var firstPeriodBalances = await GetFirstPeriodBalanceAsync(query.Period, accounts, organization);
            //balances.AddRange(firstPeriodBalances);

            //result = await HandleBalanceSheetAsync(query, balances, accounts);

            //return result;
            throw new NotImplementedException();
        }

        #region
        /// <summary>
        /// 获取年初的科目余额
        /// 如果是启用年，并且1月启用，直接拿录入的初始化期初月   如果不是1月 计算公式 借方科目：年初数=期初数+本年累计贷方-本年累计借方 贷方科目：年初数=期初数+本年累计借方-本年累计贷方
        /// 如果是非启用年 取本年的第一期的期初数
        /// </summary>
        /// <param name="period"></param>
        /// <returns></returns>
        protected async Task<List<AccountBalance>> GetFirstPeriodBalanceAsync(int period, List<Account> accounts , Organization organization)
        {
            var result = new List<AccountBalance>();

            var openingPeriod = organization.ActivatedDate?.ToPeriod();

            //如果没有启用，或者启用月份是1月，也不进行处理
            if (openingPeriod == null)
                return result;

            var openingPeriodDate = openingPeriod.Value.ToLastDayDateTime();

            //当前期间的日期
            var currentPeriodDate = period.ToFirstDayDateTime();

            //当前年的第一个期间
            var currentYearFirstPeriod = currentPeriodDate.Year * 100 + 1;

            //如果查询的期间和本年第一期是一样的，就不需要去查询年初余额
            if (currentYearFirstPeriod == period)
                return result;

            //如果第一期等于启用期间，直接取年初的金额
            if (currentYearFirstPeriod == openingPeriod)
            {
                var balances = await _accountBalanceRepository.FindAsync(new AccountBalanceQuery() { Period = currentYearFirstPeriod, IncludeCurrency = false, IncludeDimension = false }); 

                return balances;
            }

            //非启用年或者启用年月份等于1，直接拿该年第一期的作为期初余额
            if (currentPeriodDate.Year != openingPeriodDate.Year || openingPeriodDate.Month == 1)
            {
                var balances = await _accountBalanceRepository.FindAsync(new AccountBalanceQuery() { Period = currentYearFirstPeriod, IncludeCurrency = false, IncludeDimension = false });

                return balances;
            }
            else if (currentPeriodDate.Year == openingPeriodDate.Year && openingPeriodDate.Month != 1)
            {
                //启用年 并且非1月启用
                var initalizePeriod = openingPeriod.Value.ToPrePeriod();

                var initalizeBalances = await _accountBalanceRepository.FindAsync(new AccountBalanceQuery() { Period = initalizePeriod, IncludeCurrency = false, IncludeDimension = false });

                //开始组装该年第一期数据

                foreach (var balance in initalizeBalances)
                {
                    var matchAccount = accounts.FirstOrDefault(x => x.Id == balance.AccountId);

                    if (matchAccount == null)
                        continue;

                    var openingAmount = matchAccount.Direction == Enums.AccountDirectionType.Debit ? (balance.OpeningAmount ?? 0) - (balance.CurrentYearDebitAmount ?? 0) + (balance.CurrentYearCreditAmount ?? 0) :
                        balance.OpeningAmount - (balance.CurrentYearCreditAmount ?? 0) + (balance.CurrentYearDebitAmount ?? 0);

                    balance.ChangeOpeningAmount(openingAmount, balance.OpeningAmountFr);

                    balance.ChangeCurrentAmount(null, null, null, null);

                    balance.ChangePeriod(currentYearFirstPeriod);

                }

                result = initalizeBalances;
            }

            return result ?? new List<AccountBalance>();
        }


        /// <summary>
        /// 处理资产负债表数据
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="balances"></param>
        /// <param name="accounts"></param>
        /// <returns></returns>
        private async Task<List<BalanceSheetReportDto>> HandleBalanceSheetAsync(BalanceSheetReportQuery filter, List<AccountBalance> balances, List<Account> accounts)
        {
            var reportItems = await _financialReportItemRepository.FindAsync(new FinancialReportItemQuery() { ReportType = Enums.FinancialReportType.BalanceSheet });

            var result = new List<BalanceSheetReportDto>();

            if (filter.ReportItemCodes !=null)
                reportItems = reportItems.Where(x => filter.ReportItemCodes.Contains(x.Code)).ToList();

            if (reportItems.Count == 0)
                return result;

            //只需要综合本位币数据

            var currentPeriodBalances = balances.Where(x => x.Period == filter.Period).ToList();

            //该年的第一期数据
            var currentYearFirstPeriod = filter.Period.ToLastDayDateTime().Year * 100 + 1;

            var opeingPeriodBalances = balances.Where(x => x.Period == currentYearFirstPeriod).ToList();

            //资产项
            var assetReportItems = reportItems.Where(x => x.AssociateCode.IsNullOrEmpty()).ToList();

            var outputAssetReportItmes = ObjectMapper.Map<List<FinancialReportItem>, List<FinancialReportItemDto>>(assetReportItems);

            var liabilitiesReportItems = reportItems.Where(x => !x.AssociateCode.IsNullOrEmpty()).ToList();

            var outputLiabilitiesReportItems = ObjectMapper.Map<List<FinancialReportItem>, List<FinancialReportItemDto>>(liabilitiesReportItems);

            foreach (var reportItem in assetReportItems)
            {
                var row = new BalanceSheetReportDto();

                row.AssetItemId = reportItem.Id;
                row.AssetItemName = reportItem.Name;
                row.AssetItemNumber = reportItem.Number;

                var outputMatchAssetReportItem = outputAssetReportItmes.FirstOrDefault(x => x.Id == reportItem.Id);
                //期末数
                row.AssetItemClosingAmount = await GetReportItemAmount(4, outputMatchAssetReportItem, accounts, currentPeriodBalances, outputAssetReportItmes);


                var matchLiabilitiesReportItem = liabilitiesReportItems.FirstOrDefault(x => x.AssociateCode == reportItem.Code);

                row.LiabilitiesItemId = matchLiabilitiesReportItem?.Id;
                row.LiabilitiesItemName = matchLiabilitiesReportItem?.Name;
                row.LiabilitiesItemNumber = matchLiabilitiesReportItem?.Number;

                var outputMatchLiabilitiesReportItem = matchLiabilitiesReportItem == null ? null : outputLiabilitiesReportItems.FirstOrDefault(x => x.Id == matchLiabilitiesReportItem.Id);

                //期末数
                row.LiabilitiesItemClosingAmount = await GetReportItemAmount(4, outputMatchLiabilitiesReportItem, accounts, currentPeriodBalances, outputLiabilitiesReportItems);

                result.Add(row);
            }


            //计算年初数
            foreach (var reportItem in assetReportItems)
            {
                var outputResult = result.FirstOrDefault(x => x.AssetItemId == reportItem.Id);

                if (outputResult == null)
                    continue;

                var outputMatchAssetReportItem = outputAssetReportItmes.FirstOrDefault(x => x.Id == reportItem.Id);

                //年初数
                outputResult.AssetItemOpeningAmount = await GetReportItemAmount(3, outputMatchAssetReportItem, accounts, opeingPeriodBalances, outputAssetReportItmes);

                var matchLiabilitiesReportItem = liabilitiesReportItems.FirstOrDefault(x => x.AssociateCode == reportItem.Code);

                var outputMatchLiabilitiesReportItem = matchLiabilitiesReportItem == null ? null : outputLiabilitiesReportItems.FirstOrDefault(x => x.Id == matchLiabilitiesReportItem.Id);

                //年初数
                outputResult.LiabilitiesItemOpeningAmount = await GetReportItemAmount(3, outputMatchLiabilitiesReportItem, accounts, opeingPeriodBalances, outputLiabilitiesReportItems);
            }

            return result;
        }
        #endregion
    }
}
